package com.hang.jvm.risc;

/**
 * 指令解码
 */
public class InstructionDecoder {
    private RegisterFile registerFile;
    private Memory memory;


    public InstructionDecoder(RegisterFile registerFile, Memory memory) {
        this.registerFile = registerFile;
        this.memory = memory;
    }

    public void decode(long instruction) {
        int opcode = (int) (instruction & 0x7F);
        int rd = (int) ((instruction >> 7) & 0x1F);
        int rs1 = (int) ((instruction >> 15) & 0x1F);
        int rs2 = (int) ((instruction >> 20) & 0x1F);
        int funct3 = (int) ((instruction >> 12) & 0x7);
        int funct7 = (int) ((instruction >> 25) & 0x7F);
        long imm = (instruction >> 20) & 0xFFF;

        switch (opcode) {
            case 0x33:  // R-type
                executeRType(rd, rs1, rs2, funct3, funct7);
                break;
            case 0x13: // I-type
                executeIType(rd, rs1, imm, funct3);
                break;
            case 0x23: // S-type
                executeSType(rs1, rs2, imm);
                break;
            case 0x63: // B-type
                executeBType(rs1, rs2, imm, funct3);
                break;
            case 0x37: // U-type
                executeUType(rd, imm);
                break;
            case 0x6F: // J-type
                executeJType(rd, imm);
                break;
            default:
                throw new UnsupportedOperationException("Unknown opcode: " + opcode);
        }
    }

    private void executeJType(int rd, long imm) {
    }

    private void executeUType(int rd, long imm) {
        registerFile.write(rd, imm << 12);
    }

    private void executeBType(int rs1, int rs2, long imm, int funct3) {
        long val1 = registerFile.read(rs1);
        long val2 = registerFile.read(rs2);
        boolean branch = false;

        switch (funct3) {
            case 0x0: // BEQ
                branch = (val1 == val2);
                break;
            case 0x1: // BNE
                branch = (val1 != val2);
                break;
            case 0x4: // BLT
                branch = (val1 < val2);
                break;
            case 0x5: // BGE
                branch = (val1 >= val2);
                break;
            case 0x6: // BLTU
                branch = ((val1 & 0xFFFFFFFFFFFFFFFFL) < (val2 & 0xFFFFFFFFFFFFFFFFL));
                break;
            case 0x7: // BGEU
                branch = ((val1 & 0xFFFFFFFFFFFFFFFFL) >= (val2 & 0xFFFFFFFFFFFFFFFFL));
                break;
        }

        if (branch) {
            // TODO: Implement branch logic
        }
    }

    private void executeSType(int rs1, int rs2, long imm) {
        long val1 = registerFile.read(rs1);
        long val2 = registerFile.read(rs2);
        long address = val1 + imm;
        memory.storeWord(address, val2);
    }

    private void executeIType(int rd, int rs1, long imm, int funct3) {
        long val1 = registerFile.read(rs1);
        long result = 0;

        switch (funct3) {
            case 0x0: // ADDI
                result = val1 + imm;
                break;
            case 0x1: // SLLI
                result = val1 << (imm & 0x3F);
                break;
            case 0x2: // SLTI
                result = (val1 < imm) ? 1 : 0;
                break;
            case 0x3: // SLTIU
                result = ((val1 & 0xFFFFFFFFFFFFFFFFL) < (imm & 0xFFFFFFFFFFFFFFFFL)) ? 1 : 0;
                break;
            case 0x4: // XORI
                result = val1 ^ imm;
                break;
            case 0x5: // SRLI/SRAI
                if ((imm & 0x3F) == 0x00) {
                    result = val1 >>> (imm & 0x3F);
                } else if ((imm & 0x3F) == 0x20) {
                    result = val1 >> (imm & 0x3F);
                }
                break;
            case 0x6: // ORI
                result = val1 | imm;
                break;
            case 0x7: // ANDI
                result = val1 & imm;
                break;
        }

        registerFile.write(rd, result);
    }

    private void executeRType(int rd, int rs1, int rs2, int funct3, int funct7) {
        long val1 = registerFile.read(rs1);
        long val2 = registerFile.read(rs2);
        long result = 0;

        switch (funct3) {
            case 0x0: // ADD/SUB
                if (funct7 == 0x00) {
                    result = val1 + val2;
                } else if (funct7 == 0x20) {
                    result = val1 - val2;
                }
                break;
            case 0x1: // SLL
                result = val1 << (val2 & 0x3F);
                break;
            case 0x2: // SLT
                result = (val1 < val2) ? 1 : 0;
                break;
            case 0x3: // SLTU
                result = ((val1 & 0xFFFFFFFFFFFFFFFFL) < (val2 & 0xFFFFFFFFFFFFFFFFL)) ? 1 : 0;
                break;
            case 0x4: // XOR
                result = val1 ^ val2;
                break;
            case 0x5: // SRL/SRA
                if (funct7 == 0x00) {
                    result = val1 >>> (val2 & 0x3F);
                } else if (funct7 == 0x20) {
                    result = val1 >> (val2 & 0x3F);
                }
                break;
            case 0x6: // OR
                result = val1 | val2;
                break;
            case 0x7: // AND
                result = val1 & val2;
                break;
        }

        registerFile.write(rd, result);
    }
}
